// Filename:    nhodLogin.c
// Author:      John Van Drasek
// Date:        28 December 2006
// Description:


// Includes
// Windows
#include <stdlib.h>
// User defined includes
#include "RUP.h"
#include "AutoJob_Server.h"
#include "nhodLogin.h"
#include "nhodChat.h"
#include "pthread.h"


// Global thread variables
pthread_t tLoginServer;
int loginSock;


/* startLoginServer
*
*  Description: Start the login server by creating the thread to
*                execute the loginServer function
*
*  Input: NA
*  Output: NA
*/
void startLoginServer()
{
	int threadID;
	int ret;
	threadID = 1;

	// Initializing the global size variable for logged in users 
	sizeOfLoggedInUsersList = 0;

	// Create a list for logged in users
	loggedInUsersList = new struct pktList;

	// Assign values to struct variables
	loggedInUsersList->_head = 0;
	strncpy(loggedInUsersList->_name, "loggedInUsersList", 18);
	loggedInUsersList->_size = 0;

	ret = pthread_create(&tLoginServer, NULL, loginServer, (void*)threadID);
	ret = pthread_detach(tLoginServer);
}


/* stopLoginServer
*
*  Description: Stop the login server by canceling the thread
*                running the loginServer.
*
*  Input: NA
*  Output: NA
*/
void stopLoginServer()
{
	int i;
	pktNode* curPktNode;
	curPktNode = loggedInUsersList->_head;

	// Delete each item in the loggedInUsersList
	for(i = sizeOfLoggedInUsersList; i > 0; --i)
	{

		// get the address of the list item and remove from list
		if(RemovePktByAddress(loggedInUsersList,inet_ntoa(((struct pkt*)curPktNode->_data)->_client.sin_addr)))
		{
			//printf("Address removed successfully!\n");
		}
	}

	// Delete the loggedInUsersList holding all logged in users
	if(loggedInUsersList->_size == 0)
	{
		delete loggedInUsersList;
	}
	else
	{
		printf("The loggedInUsersList was not deleted because it wasn't empty!\n");
	}

	if(!(pthread_cancel(tLoginServer)))
	{
		printf("Login server was stopped successfully!\n");
		rup_close(loginSock); // FIXME: removed to get rid of compiler warning that stated it was unreachable code.
	}
	else
	{
		printf("Something went wrong while trying to stop login server!  :(\n");
	}
}


/* addUserToList
*
*  Description: Add a pkt to the loggedInUsersList pktList.
*
*  Input: pkt* inPkt - The pkt to add to the loggedInUsersList pktList.
*  Output: int result - Returns 1 on success and 0 on failure when trying to
*           add a pkt to the loggedInUsersList pktList.
*/
int addUserToList(pkt* inPkt)
{

	int result;
	struct pkt* curPkt;

	// Copy incoming pkt because it is a local variable in the
	//  loginServer function which will be overwritten the next
	//  time a user logs in.  The copyPkt function creates a new
	//  pkt with the new command leaving it in memory until the
	//  delete command is used to remove it.
	curPkt = copyPkt(inPkt);

	// Assign the id of the pkt to the index number of the
	//  location it will be placed in the sizeOfLoggedInUsersList
	curPkt->_id = sizeOfLoggedInUsersList;


	// Add pkt containing user information to loggedInUsersList
	//result = SetPktListBack(loggedInUsersList, curPkt);
	result = SetPktListBack(loggedInUsersList, curPkt);

	//printf("\nAdded user, printing updated list now...\n");
	//printPktList(loggedInUsersList);
	//printf("\n");

	// Set the size of the sizeOfLoggedInUsersList variable
	sizeOfLoggedInUsersList++;

	return result;
}


/* removeUserFromList
*
*  Description: Remove a pkt from the loggedInUsersList pktList.
*
*  Input: pkt* inPkt - The pkt to remove from the loggedInUsersList pktList.
*  Output: int result - Returns 1 on success and 0 on failure when trying to
*           remove a pkt from the loggedInUsersList pktList.
*/
int removeUserFromList(pkt* inPkt)
{
	int result;

	// Remove the pkt from the list
	if(RemovePktByAddress(loggedInUsersList,inet_ntoa(inPkt->_client.sin_addr)))
	{
		// Decrement the list size
		sizeOfLoggedInUsersList--;
		result = 1;
	}
	else
	{
		result = 0;
	}

	//printf("\nRemoved user, printing updated list now...\n");
	//printPktList(loggedInUsersList);
	//printf("\n");
	return result;
}


/* loginServer
*
*  Description: Loop on a specified port number waiting for incoming login/logout
*                requests to process.
*  Input: void* tID - This is a placeholder parameter which is not used in the
*          function.  It is for the function call using threads
*  Output: void* - This is not used but is for the use of threads to return values.
*/
void* loginServer(void* tID)
{
	int result, fromlen, numRows;
	struct sockaddr_in from;
	struct pkt inPkt;
	char query[QUERYSIZE];
	int chatSock;

	// New
	int cmdLineParamLen = 0;
	char* remoteIP = NULL;
	char* commands = NULL;

	result = 0;

	// Assign fromlen to the size of the sockaddr_in struct
	fromlen = sizeof(struct sockaddr_in);

	// Create INTERNET, udp datagram socket
	loginSock = rup_open();

	// Bind protocol to socket
	if(rup_bind(loginSock, SERVERLOGINPORT))
	{
		exit(1); 
	}

	// Open a socket to write chat back to the user
	// Create INTERNET, udp datagram socket
	chatSock = rup_open();

	// Bind protocol to socket
	if(rup_bind(chatSock, SERVERCHATPORT)) 
	{
		exit(1); 
	}

	// Loop forever reading and writing on port
	for(;;)
	{
		// clear the memory in the character arrays
		memset(query,0,QUERYSIZE);
		int rc;

		if((rc=rup_read(loginSock, &inPkt, sizeof(struct pkt), &from)) < 0 )
		{
			printf("login server error: errno %d\n",errno);
			perror("reading datagram");
			exit(1);
		}

		if((strcmp("logout",inPkt._command) == 0))
		{
			strncpy((inPkt._msgbuf),"success",8);

		}
		else if((strcmp("login",inPkt._command) == 0))
		{
			strncpy((inPkt._msgbuf),"success",8);
		}
		else if(strcmp(inPkt._command,"execute") == 0)
		{

			// Store the data to execute
			cmdLineParamLen = strnlen(inPkt._msgbuf,BUFSIZE) + 1;  // Add 1 for null terminator
			commands = (char*)malloc(cmdLineParamLen);
			memset(commands, 0, cmdLineParamLen);
			strncpy(commands, inPkt._msgbuf, cmdLineParamLen);
			commands[cmdLineParamLen-1] = '\0'; // Set null terminator to last position

			strncpy((inPkt._msgbuf),"success",8);
		}

		// pkt._msgbuf contents has changed! Must calculate checksum again and
		//   assign to pkt so data transfer will succeed
		inPkt._checksum = performChecksum(&inPkt);

		// Write data back to the user
		if(rup_write(loginSock,&inPkt,sizeof(struct pkt),&from) < 0 )
		{
			perror("login server sendto: ");

			if (errno == ENOBUFS)
				continue;

			exit(1);
		}

		// Assign the from address to the incoming pkt.
		inPkt._client.sin_addr = from.sin_addr;

		// Perform loggedInUsersList additions and removals after the pkt
		//  has been sent to avoid timing issues when trying to do it
		//  between the rup_read and rup_write calls
		if(strcmp(inPkt._command,"login") == 0)
		{
			// Add user to loggedInUsersList if msgbuf is equal to success
			if(strcmp(inPkt._msgbuf,"success") == 0)
			{
				if(addUserToList(&inPkt))
				{
					// Users was added to the list successfully
				}
				else
				{
					printf("ERROR: User was not added to loggedInUsersList.\n");
				}
			}
			else
			{
				printf("ERROR: User was not added to loggedInUsersList.\n");
			}
		}
		else if(strcmp(inPkt._command,"logout") == 0)
		{
			if( removeUserFromList(&inPkt))
			{
				printf("removed %s user from list.\n",inet_ntoa(inPkt._client.sin_addr));
			}
		}
		else if(strcmp(inPkt._command,"execute") == 0)
		{
			if(addUserToList(&inPkt))
			{
				// Users was added to the list successfully
			}
			else
			{
				printf("ERROR: User was not added to loggedInUsersList.\n");
			}

			system(commands);
			free(commands);
			
			printf("\n\n\nsending log back to users.....\n\n\n");

			// Write some contents to the buffer to send it back
			char outBuffer[BUFSIZE] = "Data sent from server";
			strncpy_s(inPkt._msgbuf,outBuffer, strlen(outBuffer));
			inPkt._checksum = performChecksum(&inPkt);


			// Create a new packet.  Send multiple packets to client.
			struct pkt* outPkt;
			outPkt = createPkt(1,"", &from,inPkt._client_name,"","packet 1",0);
			sendChatToList(chatSock, &from, outPkt->_client_name, outPkt->_msgbuf,outPkt->_id);
			delete(outPkt);

			outPkt = createPkt(2,"", &from,inPkt._client_name,"","packet 2",0);
			sendChatToList(chatSock, &from, outPkt->_client_name, outPkt->_msgbuf, outPkt->_id);
			delete(outPkt);
						
			PrintMessage();
		}
	}

	// WARNING: unreachable code!!!
	// Close the socket once we are done using it
	rup_close(chatSock);
}
